package net.xiaosaguo.mymall.order.controller;

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * description: 接口幂等性保证，使用 token，token 往页面、redis 分别存一份
 *
 * @author xiaosaguo
 * @date 2020/07/11
 */
@RestController
public class IdempotentTokenController {

    public static final String USER_ORDER_TOKEN_PREFIX = "order:token:";
    public static final String LOGIN_USER_ID = "1";

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @GlobalTransactional
    @GetMapping("/test")
    public String test() {
        String token = UUID.randomUUID().toString();
        stringRedisTemplate.opsForValue().set(USER_ORDER_TOKEN_PREFIX + LOGIN_USER_ID, token, 30, TimeUnit.MINUTES);
        return token;
    }

    @GetMapping("/valid")
    public String validToken(String token) {
        // 1. 验证令牌，核心：令牌的对比和删除必须保证原子性
        String script = "if redis.call('get',KEY[1]) == ARGV[1] then return redis.call('del',KEY[1]) else return 0 end";
        // script 执行结果 0/1
        Long execute = stringRedisTemplate.execute(
                new DefaultRedisScript<>(script),
                Collections.singletonList(USER_ORDER_TOKEN_PREFIX + LOGIN_USER_ID),
                token
        );
        if (execute != null && execute == 1) {
            // 执行业务逻辑
            return "ok";
        } else {
            // 令牌验证失败，就提示不能重复提交，或者重定向到原始页面
            return "已处理，请勿重复提交";
        }
    }
}
